package com.ys.HashMap初级解析;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 描述:
 * * 1.HashMap类的介绍
 *  *   HashMap在“java.util”包中，是Map接口的实现类，也就意味着Map中的方法，那么HashMap都能使用。
 *  *   HashMap要求key不能重复，如果添加键值对的时候发生key重复，则执行覆盖操作。为了保证key不重复，
 *  *   那么我们重写key所对应的类的hashCode()与equals()方法来保证Key的唯一性。
 *  *   重点：key对应的类，我们必须重写hashCode()与equals()方法。
 *  * 2.hashCode()与equals()方法的总结
 *  *   结论1：如果两个对象调用equals()方法返回的结果为true，那么这两个对象调用hashCode()返回的结果肯定相同。
 *  *          如果没有重写hashCode()与equals()方法，那么自动满足以上条件。
 *  *          如果重写equals()方法，那么必须重写hashCode()方法，以保证以上条件成立。
 *  *   结论2：两个对象调用hashCode()返回的结果相同，那么这两个对象调用equals()方法返回的结果未必为true。
 *  *          原因：因为不同的对象的哈希算法返回的结果可能重复。
 *  * 3.HashMap的具体使用
 *  * 3.1 Key的类型为Java提供的类
 *  *     例如：包装类、String类等，都属于Java提供的类，那么他们都默认重写hashCode()与equals()方法。
 *  * 3.2 Key的类型为自定义的类
 *  *     要求：自定义的类，我们必须重写hashCode()与equals()方法来保证Key的唯一性。
 *  * 4.HashMap的特点
 *  *   HashMap底层采用是“哈希表（散列表）”来实现，哈希表的“增、删、改和查”的效率都非常高，并且存储的数据是“无序”的。
 * * 1.哈希表的引入
 *  *   回顾“数组”和“链表”的优势和劣势
 *  *      数组：根据索引“查找”效率高，但是“插入”和“删除”效率非常低。
 *  *      链表：根据序号“查找”效率非常低，但是“插入”和“删除”效率非常高。
 *  *   有没有一种数据结构，能够兼容“数组”和“链表”的优势呢？？？
 *  *      哈希表，也称之为散列表。
 *  * 2.哈希表的本质
 *  *   哈希表的本质就是“数组”+“链表”的组合，当然哈希表还有别的实现方式，但是“数组”+“链表”的实现方式是最常见的。
 *  * 3.分析HashMap底层原码
 *  *   HashMap底层采用的就是“哈希表”来实现，因此HashMap的“增、删、改和查”的效率都非常高，并且属于“无序”的。
 *
 * @author CodeYang
 * @version 2020/12/26 21:23
 */
public class HashMap初级引入 {

    public static void main(String[] args) {
        /** 1put(k,v) 底层默传入不是0  就调用了 key.hashcode 方法 来做散列运算 之后在 放入数组 ,
         * 如果 key.hascode % 16 得出来得 index 值重负了在做链表插入到首节点
         *  注意;  计算出得index值 ,链表长度小于8 遍历较快,大于13 建议采用红黑树
         *  16 之前 是数组+链表
         *  16 之后为数组+红黑树
         *
         *  public V put(K key, V value) {
         *         return putVal(hash(key), key, value, false, true);
         *     }
         *     static final int hash(Object key) {
         *         int h;
         *         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
         *     }
         * */
        // Java 自己提供得类基本上都提供了 重写了hashcode方法 String.hahscode 方法 根据成员遍历来做hash
        // 没有重写得话 就是根据对象在内存中得地址值来做hash算法,基本上得不到相同得
        System.out.println("1".hashCode()+"[]"+"1".hashCode());
        // 为什么要重写每个类的 hascode 方法类, 重写hashcode 会把每个成员变量都加入hashcode算法里去,这样同样的成员变量赋值相同
        // 得到hash值就是一样的 这样就可以保持 对象作为 key 时的 覆盖特性




        // key 为 自定义类
        HashMap<Student,String> map  = new HashMap<>();
        map.put(new Student("1",1),"v1");
        map.put(new Student("2",3),"v2");
        map.put(new Student("3",3),"v3");
        // 如果 不重写 hashcode 方法  new Student("1",1) 这个对象 与 前面的 new Student("1",1) hash值就是 object的hashcode在计算
        // 得到就是两个不同的hash值, 但是逻辑上他们确实一个值. 为了保持 key 的 逻辑唯一性, 应该重写他们的hashcode 方法
        map.put(new Student("1",1),"v5");
        // 重写之前 1735600054---21685669       map.put(new Student("3",3),"v3"); 不会覆盖,因为key不同
        System.out.println(new Student("1",1).hashCode() +"---"+new Student("1",1).hashCode());
        // 重写之后 return Objects.hash(name, age); 2481---2481
        System.out.println(new Student("1",1).hashCode() +"---"+new Student("1",1).hashCode());
        //
        Iterator<Map.Entry<Student, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Student, String> next = iterator.next();
            System.out.println(next.getKey()+"[]"+next.getValue());
        }

        HashMap<String, Integer> map2 = new HashMap<>();
        // 这样得 key值 调用得是String.hashcode 方法 已经重写过了,得到得hash值是一样得.
        // 所以key值一样,就会引发 key相同 做覆盖操作
        map2.put("1",1);
        map2.put("1",1);
        Set<Map.Entry<String, Integer>> entries = map2.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            System.out.println(entry);
        }


    }
}
